/*
 * Licensed to GraphHopper GmbH under one or more contributor
 * license agreements. See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 *
 * GraphHopper GmbH licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.graphhopper.jsprit.examples;

import com.graphhopper.jsprit.analysis.toolbox.GraphStreamViewer;
import com.graphhopper.jsprit.analysis.toolbox.GraphStreamViewer.Label;
import com.graphhopper.jsprit.analysis.toolbox.Plotter;
import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm;
import com.graphhopper.jsprit.core.algorithm.box.Jsprit;
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.job.Service;
import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl.Builder;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleType;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl;
import com.graphhopper.jsprit.core.reporting.SolutionPrinter;
import com.graphhopper.jsprit.core.util.Solutions;
import com.graphhopper.jsprit.io.problem.VrpXMLWriter;

import java.io.File;
import java.util.Collection;

/**
 * 简单模型
 */
public class SimpleExample {

    /**
     * Assume the following problem. We can employ one vehicle(-type) located at (10,10) with one capacity dimension, e.g. weight,
     * and a capacity value of 2 to deliver four customers located at (5,7),(5,13),(15,7),(15,13),
     * each with a demand that has a weight of 1.
     * All employed vehicles need to return to their start-locations.
     * <p>
     * 假设以下问题：
     * 我们可以使用一种位于（10,10）的车辆（类型），其一个容量尺寸（例如重量）和容量值为2，
     * 以交付四个位于[（5,7），（5,13），（ 15,7），（15,13）]，每个需求的权重为1。
     * 所有雇用的车辆都需要返回其起始位置。
     */
    public static void main(String[] args) {
        /*
         * some preparation - create output folder
         * 创建输出文件夹
         */
        File dir = new File("output");
        // if the directory does not exist, create it
        if (!dir.exists()) {
            System.out.println("creating directory ./output");
            boolean result = dir.mkdir();
            if (result) {
                System.out.println("./output created");
            }
        }

        /*
         * get a vehicle type-builder and build a type with the typeId "vehicleType" and one capacity dimension, i.e. weight, and capacity dimension value of 2
         * 创建一个车辆类型，名称为"vehicleType"，容量纬度为2，规格如下：
         *  id = vehicleType
         *  capacity dimension = 2
         */
        final int weightIndex = 0;
        VehicleType vehicleType = VehicleTypeImpl.Builder.newInstance("vehicleType")
            .addCapacityDimension(weightIndex, 2)
            .build();

        /*
         * get a vehicle-builder and build a vehicle located at (10,10) with type "vehicleType"
         * 在坐标(10,10)处创建一个上述类型的车辆，名称为"vehicle"。规格如下：
         *  id = vehicle
         * location = [10, 10]
         * type = vehicleType
         *
         */
        VehicleImpl vehicle = VehicleImpl.Builder.newInstance("vehicle")
            .setStartLocation(Location.newInstance(10, 10))
            .setType(vehicleType)
            .build();

        /*
         * build services at the required locations, each with a capacity-demand of 1.
         * 在所需位置构建ID为1 ... 4的服务，规格如下：
         *  capacity dimension = 1
         *  time windows = [0.0, Double.MAX_VALUE]
         *  location = [x, y]
         */
        Service service1 = Service.Builder.newInstance("1")
            .addSizeDimension(weightIndex, 1)
            .setLocation(Location.newInstance(10, 30))
            .build();
        Service service2 = Service.Builder.newInstance("2")
            .addSizeDimension(weightIndex, 1)
            .setLocation(Location.newInstance(0, 50))
            .build();
        Service service3 = Service.Builder.newInstance("3")
            .addSizeDimension(weightIndex, 1)
            .setLocation(Location.newInstance(10, -17))
            .build();
        Service service4 = Service.Builder.newInstance("4")
            .addSizeDimension(weightIndex, 1)
            .setLocation(Location.newInstance(-32, 17))
            .build();
        Service service5 = Service.Builder.newInstance("5")
            .addSizeDimension(weightIndex, 1)
            .setLocation(Location.newInstance(-10, -10))
            .build();
        Service service6 = Service.Builder.newInstance("6")
            .addSizeDimension(weightIndex, 1)
            .setLocation(Location.newInstance(-32, -42))
            .build();
        Service service7 = Service.Builder.newInstance("7")
            .addSizeDimension(weightIndex, 1)
            .setLocation(Location.newInstance(25, 31))
            .build();

        /*
         * build the problem
         * by default, the problem is specified such that FleetSize is INFINITE, i.e. an infinite number of
         * the defined vehicles can be used to solve the problem
         * by default, transport costs are computed as Euclidean distances
         * 创建VRP模型：
         *  默认情况下，问题的指定方式是FleetSize为INFINITE，即：可使用无限数量的定义的车辆来解决问题
         *  默认情况下，运输成本以欧几里得距离计算
         */
        VehicleRoutingProblem problem = VehicleRoutingProblem.Builder.newInstance()
            .addVehicle(vehicle)
            .addJob(service1)
            .addJob(service2)
            .addJob(service3)
            .addJob(service4)
            .addJob(service5)
            .addJob(service6)
            .addJob(service7)
            .build();

        /*
         * get the algorithm out-of-the-box.
         * 使用开箱即用算法
         *
         */
        VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(problem);

        /*
         * and search a solution which returns a collection of solutions (here only one solution is constructed)
         * 查找所有的解的集合 TODO 明天继续... hahaha~~~~
         */
        Collection<VehicleRoutingProblemSolution> solutions = algorithm.searchSolutions();

        /*
         * use the static helper-method in the utility class Solutions to get the best solution (in terms of least costs)
         * 选取一个最优解
         */
        VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions);

        /*
         * If you want to write out problem and solution, you require the writer that is located in jsprit-io.
         * Thus, you need to add the corresponding module to your pom.xml much like you added jsprit-core.
         * Just use jsprit-io. You can then use this:
         * 是否需要作图，需要jsprit-io包
         */
        new VrpXMLWriter(problem, solutions).write("output/problem-with-solution.xml");

        /*
         * 日志级别，精简还是详细
         */
        SolutionPrinter.print(problem, bestSolution, SolutionPrinter.Print.VERBOSE);

        /*
        If you want to further analyse your solution, add the module jsprit-analysis to your pom. Then you can plot the routes like this
         * 是否需要分析，并需要保存图片。需要jsprit-analysis包
         */
        new Plotter(problem, bestSolution).plot("output/plot.png", "simple example");

        /*
        use the GraphStreamViewer which dynamically renders the problem and its according solution by coding
        自动渲染图片
         */
        new GraphStreamViewer(problem, bestSolution).labelWith(Label.ID).setRenderDelay(200).display();
    }

}
